/* Foley C# Utilities
 * Copyright (C)2002 Rodney S. Foley
 *  
 * This program is free software; you can 
 * redistribute it and/or modify it under the 
 * terms of the GNU General Public License as 
 * published by the Free Software Foundation; 
 * either version 2 of the License, or (at your 
 * option) any later version.
 * 
 * This program is distributed in the hope that 
 * it will be useful, but WITHOUT ANY WARRANTY; 
 * without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR 
 * PURPOSE. See the GNU General Public License 
 * for more details.
 * 
 * You should have received a copy of the GNU 
 * General Public License along with this 
 * program; if not, write to: 
 * 
 *		Free Software Foundation, Inc., 
 *		59 Temple Place, Suite 330, 
 *		Boston, MA 02111-1307 USA
 * 
 *					- or -
 * 
 * http://opensource.org/licenses/gpl-license.php
 */

using System;
using System.Collections;

namespace Foley.Utilities.Collections
{
	/// <summary>
	/// A Doubly-Linked Set implementation extending the abstract <see cref="SetBase"/> class.
	/// </summary>
	public class LinkedSet : SetBase, ICloneable
	{
		#region Private Member Variables
		private LinkedList list;
		#endregion

		#region Public Constructors
		/// <summary>
		/// Initializes a new instance of the <see cref="LinkedSet"/> class that is empty.
		/// </summary>
		public LinkedSet()
		{
			list = new LinkedList();
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="LinkedSet"/> class that contains 
		/// elements copied from the specified <paramref name="collection"/>.
		/// </summary>
		/// <param name="collection">
		/// The <see cref="ICollection"/> whose elements are copied to the new list.
		/// </param>
		public LinkedSet(ICollection collection) : this()
		{
			Add(collection);
		}
		#endregion

		#region Public Member Properties
		/// <summary>
		/// Gets the number of elements contained in the <see cref="LinkedSet"/>.
		/// </summary>
		/// <value>
		/// The number of elements actually contained in the <see cref="LinkedSet"/>.
		/// </value>
		public override int		Count	 { get { return list.Count; } }

		/// <summary>
		/// Gets a boolean value indicating whether the <see cref="LinkedSet"/> is empty 
		/// or not.
		/// </summary>
		/// <value>
		/// <c>true</c> if the <see cref="LinkedSet"/> contains no elements; 
		/// otherwise, <c>false</c>.
		/// </value>
		public override bool	IsEmpty	 { get { return list.IsEmpty; } }

		/// <summary>
		/// Gets an <see cref="object"/> that can be used to synchronize access to the <see cref="LinkedSet"/>.
		/// </summary>
		/// <value>
		/// An <see cref="object"/> that can be used to synchronize access to the <see cref="LinkedSet"/>.
		/// </value>
		public override object	SyncRoot { get { return list.SyncRoot; } }
		#endregion

		#region Public Member Methods
		/// <summary>
		/// Adds an <see cref="object"/>
		/// to the end of the <see cref="LinkedSet"/>.
		/// </summary>
		/// <param name="value">
		/// The <see cref="object"/> to be added to the end of 
		/// the <see cref="LinkedSet"/>
		/// </param>
		/// <returns>
		/// <c>true</c> if the <see cref="LinkedSet"/> is modified; otherwise <c>false</c>.
		/// </returns>
		public override bool Add(object value)
		{
			int indexAdded = -1;

			if (!Contains(value))
				indexAdded = list.Add(value);

			return 0 <= indexAdded;
		}

		/// <summary>
		/// Adds the elements of an 
		/// <see cref="ICollection"/> to the end of the <see cref="LinkedSet"/>.
		/// </summary>
		/// <param name="collection">
		/// The <see cref="ICollection"/> whose elements 
		/// should be added to the end of the <see cref="LinkedSet"/>.
		/// </param>
		/// <returns>
		/// <c>true</c> if the <see cref="LinkedSet"/> is modified; otherwise <c>false</c>.
		/// </returns>
		/// <exception cref="System.ArgumentNullException">
		/// The <paramref name="collection"/> is a <c>null</c> reference 
		/// (<c>Nothing</c> in Visual Basic).
		/// </exception>
		public override bool Add(ICollection collection)
		{
			bool beenModified = false;
			
			foreach(object value in collection)
				if (!Contains(value))
					beenModified |= (0 <= list.Add(value));						

			return beenModified;
		}

		/// <summary>
		/// Removes all elements from the <see cref="LinkedSet"/>.
		/// </summary>
		public override void Clear()
		{
			list.Clear();
		}

		/// <summary>
		/// Creates a shallow copy of the <see cref="LinkedSet"/>.
		/// </summary>
		/// <returns>
		/// A shallow copy of the <see cref="LinkedSet"/>.
		/// </returns>
		/// <remarks>
		/// If you need a deep copy to be attempted use the
		/// <see cref="LinkedSet.Clone(bool)"/> method.
		/// </remarks>
		public virtual object Clone()
		{
			LinkedSet	setClone = new LinkedSet();
			
			setClone.list = (LinkedList)list.Clone();
			
			return setClone;
		}

		/// <summary>
		/// Attempts to make a deep copy of the <see cref="LinkedSet"/>.
		/// </summary>
		/// <param name="attemptDeepCopy">
		/// If true will attempt to make a deep copy, 
		/// if false it will just return a shallow copy
		/// </param>
		/// <returns>
		/// The newly cloned <see cref="LinkedSet"/>.
		/// </returns>
		/// <exception cref="SystemException">
		/// If and <see cref="object"/> in the list is not an 
		/// <see cref="ICloneable"/>.
		/// </exception>
		/// <remarks>
		/// Attempts to make a deep copy if <paramref name="attemptDeepCopy"/> is true.
		/// It will attempt this by checking if an <see cref="object"/> is an 
		/// <see cref="ICloneable"/>, and then defer the process to its Clone method. 
		/// At the first <see cref="object"/> that is not an <see cref="ICloneable"/>
		/// a SystemException will be thrown.
		/// <para>
		/// If a <c>true</c> deep copy is important, please check the API documentation for the
		/// elements that are contained in the list to verify how their <c>Clone</c> method is
		/// implemented.
		/// </para>
		/// </remarks>
		/// <exception cref="SystemException">
		/// At the first <see cref="object"/> that is not an <see cref="ICloneable"/> 
		/// when <paramref name="attemptDeepCopy"/> is true.
		/// </exception>
		public virtual LinkedSet Clone(bool attemptDeepCopy)
		{
			LinkedSet	setClone = new LinkedSet();
			
			setClone.list = list.Clone(attemptDeepCopy);
			
			return setClone;
		}

		/// <summary>
		/// Determines whether an <see cref="object"/> is in the <see cref="LinkedSet"/>.
		/// </summary>
		/// <param name="value">
		/// The <see cref="object"/> to locate in the <see cref="LinkedSet"/>.
		/// The <see cref="object"/> to locate can be a <c>null</c> reference (<c>Nothing</c> in Visual Basic). 
		/// </param>
		/// <returns>
		/// <c>true</c> if <paramref name="value"/> is found in the <see cref="LinkedSet"/>; otherwise, <c>false</c>.
		/// </returns>
		/// <remarks>
		/// This method determines equality by calling <see cref="object.Equals"/>.
		/// </remarks>
		public override bool Contains(object value)
		{
			return list.Contains(value);
		}

		/// <summary>
		/// Determines whether all the elements in the specified 
		/// <paramref name="collection"/> are contained in the <see cref="LinkedSet"/>.
		/// </summary>
		/// <param name="collection">
		/// The <see cref="ICollection"/> whose elements are to be added
		/// to the end of the <see cref="LinkedSet"/>.
		/// </param>
		/// <returns>
		/// <c>true</c> if all the elements of the <see cref="ICollection"/> are
		/// found in the <see cref="LinkedSet"/>; otherwise, <c>false</c>.
		/// </returns>
		/// <exception cref="System.ArgumentNullException">
		/// The <paramref name="collection"/> is a <c>null</c> reference 
		/// (<c>Nothing</c> in Visual Basic).
		/// </exception>
		public override bool Contains(ICollection collection)
		{
			return list.Contains(collection);;
		}

		/// <summary>
		/// Copies the entire <see cref="LinkedSet"/> to a compatible one-dimensional <see cref="Array"/>, 
		/// starting at the specified <paramref name="index"/> of the target <paramref name="array"/>.
		/// </summary>
		/// <param name="array">
		/// The one-dimensional <see cref="Array"/> that is the destination of the elements
		/// copied from the <see cref="LinkedSet"/>. The <see cref="Array"/> must have zero-based indexing.
		/// </param>
		/// <param name="index">
		/// The zero-based <paramref name="index"/> in <paramref name="array"/> at which copying begins.
		/// </param>
		/// <exception cref="System.ArgumentNullException">
		/// The <paramref name="array"/> is a <c>null</c> reference (<c>Nothing</c> in Visual Basic).
		/// </exception>
		/// <exception cref="System.ArgumentOutOfRangeException">
		/// The <paramref name="index"/> is less than zero.
		/// </exception>
		/// <exception cref="System.ArgumentException">
		/// The <paramref name="array"/> is multidimensional.
		/// <p>-or-</p>
		/// The <paramref name="index"/> is equal to or greater than the length of <paramref name="array"/>.
		/// <p>-or-</p>
		/// The number of elements in the <see cref="LinkedSet"/> is greater than
		/// the available space from <paramref name="index"/> to the end of the destination <paramref name="array"/>.
		/// </exception>
		public override void CopyTo(Array array, int index)
		{
			list.CopyTo(array, index);
		}

		/// <summary>
		/// Returns an enumerator 
		/// for the entire <see cref="LinkedSet"/>.
		/// </summary>
		/// <returns>
		/// An <see cref="IEnumerator"/> for the entire <see cref="LinkedSet"/>.
		/// </returns>
		public override IEnumerator GetEnumerator()
		{
			return list.GetEnumerator();
		}

		/// <summary>
		/// Removes the occurrence of the specific <see cref="object"/> 
		/// from the <see cref="LinkedSet"/>.
		/// </summary>
		/// <param name="value">
		/// The <see cref="object"/> to remove from the <see cref="LinkedSet"/>.
		/// </param>
		/// <returns>
		/// <c>true</c> if the <see cref="LinkedSet"/> is modified; otherwise <c>false</c>.
		/// </returns>
		public override bool Remove(object value)
		{
			return list.Remove(value);
		}

		/// <summary>
		/// Removes the occurrence of each element of the specified 
		/// <see cref="ICollection"/> from the <see cref="LinkedSet"/>.
		/// </summary>
		/// <param name="collection">
		/// The <see cref="ICollection"/> used to determine what 
		/// elements to remove from the <see cref="LinkedSet"/>.
		/// </param>
		/// <returns>
		/// <c>true</c> if the <see cref="LinkedSet"/> is modified; otherwise <c>false</c>.
		/// </returns>
		/// <exception cref="NotSupportedException">
		/// <para>The <see cref="LinkedSet"/> is read-only.</para>
		/// -or-
		/// <para>The <see cref="LinkedSet"/> has a fixed size.</para>
		/// </exception>
		/// <exception cref="System.ArgumentNullException">
		/// The <paramref name="collection"/> is a <c>null</c> reference 
		/// (<c>Nothing</c> in Visual Basic).
		/// </exception>
		public override bool Remove(ICollection collection)
		{
			return list.Remove(collection);
		}

		/// <summary>
		/// Retains the occurrence of each element 
		/// of the specified <see cref="ICollection"/> from the <see cref="LinkedSet"/>.
		/// </summary>
		/// <param name="collection">
		/// The <see cref="ICollection"/> used to determine what 
		/// elements to be retained in the <see cref="LinkedSet"/>.
		/// </param>
		/// <returns>
		/// <c>true</c> if the <see cref="LinkedSet"/> is modified; otherwise <c>false</c>.
		/// </returns>
		/// <exception cref="NotSupportedException">
		/// <para>The <see cref="LinkedSet"/> is read-only.</para>
		/// -or-
		/// <para>The <see cref="LinkedSet"/> has a fixed size.</para>
		/// </exception>
		/// <exception cref="System.ArgumentNullException">
		/// The <paramref name="collection"/> is a <c>null</c> reference 
		/// (<c>Nothing</c> in Visual Basic).
		/// </exception>
		public override bool Retain(ICollection collection)
		{
			return list.Retain(collection);
		}
		#endregion
	}
}
